import os
import abc
import asyncio
import websockets
import json
from datetime import datetime


class BaseChatClient(abc.ABC):
    """聊天客户端抽象基类"""

    def __init__(self, config_file=None):
        self.websocket = None
        self.user_id = None
        self.room_id = None
        self.server_address = None
        self.is_connected = False
        print(f"初始化客户端, 配置文件: {config_file}")
        self.load_config(config_file)

    def load_config(self, config_file="config.json"):
        """从配置文件加载服务器地址"""
        if config_file is None or not os.path.exists(config_file):
            self.config = {
                "server_address": "ws://localhost:18229",
                "user_id": f"user_{os.getpid()}",
                "room_id": "default"
            }
            with open(config_file, "w") as f:
                json.dump(self.config, f, indent=2)
            return

        print(f"加载配置文件: {config_file}")
        with open(config_file, "r") as f:
            self.config = json.load(f)

    async def connect(self, server_address, user_id, room_id="default"):
        """连接到服务器"""
        try:
            self.server_address = server_address
            self.user_id = user_id
            self.room_id = room_id

            self.websocket = await websockets.connect(server_address)

            # 发送注册信息
            register_data = {
                "type": "register",
                "user_id": self.user_id,
                "room_id": self.room_id
            }
            await self.websocket.send(json.dumps(register_data))

            self.is_connected = True
            await self.on_connected()

            # 在后台启动消息监听任务
            self._listen_task = asyncio.create_task(self._listen_for_messages())

        except Exception as e:
            await self.on_error(f"连接失败: {str(e)}")

    async def disconnect(self):
        """断开连接"""
        if self.websocket:
            await self.websocket.close()
            self.is_connected = False

            # 取消后台监听任务
            if hasattr(self, '_listen_task') and not self._listen_task.done():
                self._listen_task.cancel()
                try:
                    await self._listen_task
                except asyncio.CancelledError:
                    pass

            await self.on_disconnected()

    async def send_text_message(self, message):
        """发送文本消息"""
        if not self.is_connected:
            await self.on_error("未连接到服务器")
            return

        try:
            message_data = {
                "type": "text",
                "message": message
            }
            await self.websocket.send(json.dumps(message_data))
            await self.on_message_sent(message)
        except Exception as e:
            await self.on_error(f"发送消息失败: {str(e)}")

    async def send_file(self, file_path):
        """发送文件"""
        if not self.is_connected:
            await self.on_error("未连接到服务器")
            return

        try:
            import os
            import base64

            filename = os.path.basename(file_path)
            filesize = os.path.getsize(file_path)

            # 读取并编码文件
            with open(file_path, "rb") as f:
                file_content = f.read()
                encoded_content = base64.b64encode(file_content).decode('utf-8')

            # 发送文件消息
            file_data = {
                "type": "file",
                "filename": filename,
                "filedata": encoded_content,
                "filesize": filesize
            }
            await self.websocket.send(json.dumps(file_data))
            await self.on_file_sent(filename)
        except Exception as e:
            await self.on_error(f"发送文件失败: {str(e)}")

    async def _listen_for_messages(self):
        """监听服务器消息"""
        try:
            async for message in self.websocket:
                message_data = json.loads(message)
                await self._handle_message(message_data)
        except websockets.exceptions.ConnectionClosed:
            self.is_connected = False
            await self.on_disconnected()
        except Exception as e:
            await self.on_error(f"接收消息时出错: {str(e)}")

    async def _handle_message(self, message_data):
        """处理接收到的消息"""
        message_type = message_data.get("type")

        if message_type == "text":
            # 文本消息
            msg = f"[{message_data['timestamp']}] {message_data['sender']}: {message_data['message']}"
            await self.on_text_message_received(msg, message_data['sender'], message_data['message'])

        elif message_type == "file":
            # 文件消息
            await self.on_file_received(message_data)

        elif message_type == "system":
            # 系统消息
            msg = f"[{message_data['timestamp']}] 系统: {message_data['message']}"
            await self.on_system_message_received(msg, message_data['message'])

        elif message_type == "user_list":
            # 用户列表
            users = message_data.get("users", [])
            await self.on_user_list_updated(users)

        elif message_type == "error":
            # 错误消息
            msg = f"[{message_data['timestamp']}] 错误: {message_data['message']}"
            await self.on_error(msg)

    # 抽象方法，子类必须实现
    @abc.abstractmethod
    async def on_connected(self):
        """连接成功时调用"""
        pass

    @abc.abstractmethod
    async def on_disconnected(self):
        """断开连接时调用"""
        pass

    @abc.abstractmethod
    async def on_text_message_received(self, formatted_message, sender, raw_message):
        """收到文本消息时调用"""
        pass

    @abc.abstractmethod
    async def on_file_received(self, file_data):
        """收到文件时调用"""
        pass

    @abc.abstractmethod
    async def on_system_message_received(self, formatted_message, raw_message):
        """收到系统消息时调用"""
        pass

    @abc.abstractmethod
    async def on_user_list_updated(self, users):
        """用户列表更新时调用"""
        pass

    @abc.abstractmethod
    async def on_message_sent(self, message):
        """消息发送成功时调用"""
        pass

    @abc.abstractmethod
    async def on_file_sent(self, filename):
        """文件发送成功时调用"""
        pass

    @abc.abstractmethod
    async def on_error(self, error_message):
        """发生错误时调用"""
        pass